#include "fonctions.h"

struct connexion maConnexion;

int MonAccept(int sockfd,struct sockaddr *addr,socklen_t *addrlen) {
	struct tcp_packet *paquetRecu = malloc(sizeof(struct tcp_packet));
	struct tcp_packet paquetAEnvoyer;
	int n = 0, socketDATA = 0;
	maConnexion.num_seq = 1; // Initialisation

	//printf("Attente d'un paquet SYN ...\n");
	n = recvfrom(sockfd, paquetRecu, sizeof(struct tcp_packet), 0, addr, addrlen);
	if(n == -1) {
		perror("recvfrom() error");return -1;}
		if(typePaquet(*paquetRecu) == SYN) { //Un paquet SYN est reçu
			//printf("... Reception paquet SYN\n");
			modifierEtatTCP(SYNRECEIVED);

			//printf("Creation de la nouvelle socket pour le client\n");
			socketDATA = creerSocket("0", paquetRecu->port_destination + 1);

			//printf("Creation du paquet SYN+ACK\n");
			paquetAEnvoyer = creerPaquetTCP(paquetRecu->port_destination+1, paquetRecu->port_source, (paquetRecu->numero_sequence) + 2 , (paquetRecu->numero_sequence) + 1, 0, SYNACK, NULL);
			
			//printf("-> Envoi du paquet SYN+ACK\n");
			if(sendto(sockfd, &paquetAEnvoyer, sizeof(struct tcp_packet), 0, addr, *addrlen) == -1) {
				perror("Erreur envoi SYN+ACK "); return -1;}

			//printf("Attente d'un paquet ACK ...\n");
			n = recvfrom(sockfd, paquetRecu, sizeof(struct tcp_packet), 0, addr, addrlen);
			if(n == -1) {
				perror("recvfrom() error");return -1;}
			else {
				if(typePaquet(*paquetRecu) == ACK) {
					//printf("... Reception paquet ACK\n");
				}

				else 
					printf("Mauvais type de paquet : %d\n", typePaquet(*paquetRecu));
			}
		}
		else {
			printf("Mauvais type de paquet : %d\n", typePaquet(*paquetRecu));
		}
			//Mise à jour des infos de connexion
			struct sockaddr_in *tmp = (struct sockaddr_in *) addr;
			maConnexion.adresse_client = tmp->sin_addr.s_addr;
			maConnexion.port_client = tmp->sin_port;
			maConnexion.adresse_serveur = inet_addr("127.0.0.1");
			maConnexion.port_serveur = htons(ntohs(paquetRecu->port_destination) + 1);
	
	return socketDATA;
}

/*
 * Intitialisation de la connexion vers le serveur
 * SYN puis ACK
 */
int MonConnect(int sockfd, struct sockaddr_in *addr) {
	int nouveauPort = 0;
	struct tcp_packet paquetSyn, paquetAck;
	struct tcp_packet *paquetRecu = malloc(sizeof(struct tcp_packet));
	socklen_t addr_len = sizeof (struct sockaddr_in);
	maConnexion.num_seq = 1; // Initialisation

	//printf("Création du paquet SYN\n");
	paquetSyn = creerPaquetTCP(1600, ntohs(addr->sin_port), maConnexion.num_seq, 0, 0, SYN, NULL);
	
	if(sendto(sockfd, &paquetSyn, sizeof(paquetSyn), 0, (struct sockaddr *) addr, (socklen_t) sizeof(struct sockaddr)) == -1)
		{perror("sendto() error");return -1;}
	//printf("-> Envoi du paquet SYN\n");
	modifierEtatTCP(SYNSENT);
	//printf("Attente du paquet SYNC+ACK ...\n");

	if(recvfrom(sockfd, paquetRecu, sizeof(struct tcp_packet), 0, (struct sockaddr *) addr, &addr_len) == -1)
		{perror("Erreur recvfrom : \n"); return -1;}
	else {
		if(typePaquet(*paquetRecu) == SYNACK) {
			//printf("... Réception du paquet SYN+ACK\n");
			nouveauPort = paquetRecu->port_source;
		}
	}

	maConnexion.adresse_serveur = addr->sin_addr.s_addr;

	//printf("Création paquet ACK\n");
	paquetAck = creerPaquetTCP(1600, addr->sin_port, 0, (paquetRecu->numero_sequence) + 1, 0, ACK, NULL);
	if(sendto(sockfd, &paquetAck, sizeof(paquetAck), 0, (struct sockaddr *) addr, (socklen_t) sizeof(struct sockaddr)) == -1)
		{perror("sendto() error (ACK)");return -1;}
	//printf("-> Envoi du paquet ACK\n");
	addr->sin_port = htons(nouveauPort); // Affectation du nouveau port
	maConnexion.port_serveur = htons(nouveauPort);
	maConnexion.port_client = htons(paquetAck.port_source);
	maConnexion.adresse_client = inet_addr("127.0.0.1");

	return sockfd;
}

/*int MonListen(int sockfd, int backlog) {
	return 0;
}*/

ssize_t MonRead(int fd, void *buf, size_t count) {
	struct tcp_packet paquetRecu;
	struct sockaddr addr;
	socklen_t addr_len = sizeof (struct sockaddr_in);
	struct tcp_packet paquetAEnvoyer;
	int n;

	printf("Attente d'un paquet à lire ...\n");

	n = recvfrom(fd, &paquetRecu, sizeof(struct tcp_packet), 0, &addr, &addr_len);
	if(n == -1) {
		perror("ERREUR - recvfrom\n");return -1;}
	else {
		if(typePaquet(paquetRecu) == DATA) { //Un paquet DATA est reçu
			//printf("... Reception d'un paquet DATA\n");

			printf("Données reçues : %s\n", paquetRecu.data);

			//Modification du numéro de séquence
			maConnexion.num_seq = paquetRecu.numero_sequence;
			
			//printf("Création du paquet ACK\n");
			paquetAEnvoyer = creerPaquetTCP(paquetRecu.port_destination, paquetRecu.port_source, 0, paquetRecu.numero_sequence + sizeof(paquetRecu.data), 0, ACK, NULL);

			//printf("-> Envoi du paquet ACK\n");
			if(sendto(fd, &paquetAEnvoyer, sizeof(struct tcp_packet), 0, &addr, addr_len) == -1) {
				perror("Erreur envoi ACK "); return -1;}

			memcpy(buf, paquetRecu.data,count);
			return count;
		}
		else if(typePaquet(paquetRecu) == FIN) {
			printf("... Reception d'un paquet FIN\n");
			modifierEtatTCP(CLOSED);
			return 0;
		}
		else {
			printf("Mauvais type de paquet\n");
		}
	}
	return -1;
}

ssize_t MonWrite(int fd, const void *buf, size_t count) {
	struct tcp_packet paquetRecu, paquetAEnvoyer = creerPaquetTCP(maConnexion.port_client, maConnexion.port_serveur, maConnexion.num_seq, 0, 0, DATA, "");
	struct sockaddr_in *sockServeur = creerSockAddr(maConnexion.port_serveur, "", 1, maConnexion.adresse_serveur);
	socklen_t addr_len = sizeof (struct sockaddr_in);
	memcpy(&paquetAEnvoyer.data, buf, count);

	printf("-> Envoi des donnnées : %s\n", (char *) buf);
	if(sendto(fd, &paquetAEnvoyer, sizeof(paquetAEnvoyer), 0, (struct sockaddr *) sockServeur, (socklen_t) sizeof(struct sockaddr)) == -1)
		{perror("Erreur sendto données : \n");return -1;}

	//Il faut se mettre en attente du ACK
	if(recvfrom(fd, &paquetRecu, sizeof(struct tcp_packet), 0, (struct sockaddr *) sockServeur, &addr_len) == -1)
		{perror("Erreur recvfrom : \n"); return -1;}
	else {
		if(typePaquet(paquetRecu) == ACK) {
			printf("Données reçues par le serveur\n");
			//Modification du numéro de séquence
			maConnexion.num_seq = paquetRecu.numero_ack;
		}
	}

	return count;
}

int MonClose(int fd) {
	struct tcp_packet paquetRecu, paquetAEnvoyer = creerPaquetTCP(maConnexion.port_client, maConnexion.port_serveur, 1, 0, 0, FIN, "");
	struct sockaddr_in *sockServeur = creerSockAddr(maConnexion.port_serveur, "", 1, maConnexion.adresse_serveur);
	socklen_t addr_len = sizeof (struct sockaddr_in);

	printf("-> Envoi d'un paquet FIN\n");
	if(sendto(fd, &paquetAEnvoyer, sizeof(paquetAEnvoyer), 0, (struct sockaddr *) sockServeur, (socklen_t) sizeof(struct sockaddr)) == -1)
		{perror("Erreur sendto données : \n");return -1;}

	//printf("Attente paquet ACK ...\n");
	//Il faut se mettre en attente du ACK
	if(recvfrom(fd, &paquetRecu, sizeof(struct tcp_packet), 0, (struct sockaddr *) sockServeur, &addr_len) == -1)
		{perror("Erreur recvfrom : \n"); return -1;}
	else {
		if(typePaquet(paquetRecu) == ACK) {
			//printf("ACK recu\n");

			if(recvfrom(fd, &paquetRecu, sizeof(struct tcp_packet), 0, (struct sockaddr *) sockServeur, &addr_len) == -1)
				{perror("Erreur recvfrom : \n"); return -1;}
			else {
				if(typePaquet(paquetRecu) == FIN) {
					//printf("FIN recu\n");

					//printf("Création du paquet ACK\n");
					paquetAEnvoyer = creerPaquetTCP(maConnexion.port_client, maConnexion.port_serveur, 1, 0, 0, ACK, "");

					//printf("-> Envoi du paquet ACK\n");
					if(sendto(fd, &paquetAEnvoyer, sizeof(paquetAEnvoyer), 0, (struct sockaddr *) sockServeur, (socklen_t) sizeof(struct sockaddr)) == -1)
						{perror("Erreur sendto données : \n");return -1;}
					printf("Fin de la connexion\n");
				}
			}
		}
	}
	return 0;
}

int closeServeur(int fd) {
	struct tcp_packet paquetRecu, paquetAEnvoyer = creerPaquetTCP(maConnexion.port_serveur, maConnexion.port_client, 1, 0, 0, ACK, "");
	struct sockaddr_in *sockClient = creerSockAddr(maConnexion.port_client, "", 1, maConnexion.adresse_client);
	socklen_t addr_len = sizeof (struct sockaddr_in);

	//printf("-> Envoi d'un paquet ACK\n");
	if(sendto(fd, &paquetAEnvoyer, sizeof(paquetAEnvoyer), 0, (struct sockaddr *) sockClient, (socklen_t) sizeof(struct sockaddr)) == -1)
		{perror("Erreur sendto données : \n");return -1;}

	//printf("Création paquet FIN\n");
	paquetAEnvoyer = creerPaquetTCP(maConnexion.port_serveur, maConnexion.port_client, 1, 0, 0, FIN, "");
	//printf("-> Envoi d'un paquet FIN \n");
	if(sendto(fd, &paquetAEnvoyer, sizeof(paquetAEnvoyer), 0, (struct sockaddr *) sockClient, (socklen_t) sizeof(struct sockaddr)) == -1)
		{perror("Erreur sendto données : \n");return -1;}

	//printf("Attente paquet ACK ...\n");
	if(recvfrom(fd, &paquetRecu, sizeof(struct tcp_packet), 0, (struct sockaddr *) sockClient, &addr_len) == -1)
		{perror("Erreur recvfrom : \n"); return -1;}
	else {
		if(typePaquet(paquetRecu) == ACK) {
			return 0;
		}
	}
	return -1;
}


/*
 * Creation d'une structure pour la connexion
 * avec le serveur
 */
struct sockaddr_in * creerSockAddr(unsigned short port, char *arg, int flag, unsigned long addr) {
	struct sockaddr_in *sock = malloc(sizeof(struct sockaddr_in));
	sock->sin_family = AF_INET;
	sock->sin_port = port;
	if(flag == 0)
		inet_pton(AF_INET, arg, &sock->sin_addr);
	else
		sock->sin_addr.s_addr = addr;

	memset(sock->sin_zero, 0, sizeof(sock->sin_zero));

	return sock;
}

struct tcp_packet creerPaquetTCP(unsigned short port_source, unsigned short port_destination, unsigned numero_sequence, unsigned numero_ack, unsigned short window, int flag_OPT, char message[1000]) {
	struct tcp_packet paquet;

	paquet.port_source = port_source;
	paquet.port_destination = port_destination;
	paquet.numero_sequence = numero_sequence;
	paquet.numero_ack = numero_ack;
	paquet.window = window;

	paquet.syn = 0;
	paquet.ack = 0;
	paquet.fin = 0;
	paquet.rst = 0;

	switch (flag_OPT)
	{
		case SYN: 
			paquet.syn = 1;
			break;

		case ACK: 
			paquet.ack = 1;
			break;

		case SYNACK: 
			paquet.syn = 1;
			paquet.ack = 1;
			break;

		case FIN : 
			paquet.fin = 1;
			break;

		case RST : 
			paquet.rst = 1;
			break;

		case DATA : 
			strcpy(paquet.data, message);
			break;

		default :
			printf("Type de paquet non reconnu");
			break;
	}

	return paquet;
}

int typePaquet(struct tcp_packet paquetAnalyse) {
	if(paquetAnalyse.rst == 1) {
		return RST;
	}
	if(paquetAnalyse.syn == 1) {
		if(paquetAnalyse.ack == 1){
			return SYNACK;
		}
		return SYN;
	}
	if(paquetAnalyse.ack == 1) {
		return ACK;
	}
	if(paquetAnalyse.fin == 1) {
		return FIN;
	}
	if(strcmp(paquetAnalyse.data, "") != 0) {
		return DATA;
	}
	return -1;
}

int creerSocket(char ipadrr[15],int port) {
	int sd = 0, opt_val = 1;
	struct sockaddr_in server_addr;

	//printf("Création d'une socket sur le port %d\n", port);

	if((sd = socket(PF_INET, SOCK_DGRAM,0)) == -1) {
		perror("Socket creation error");
		return -1;
	}
	//Bind it
	setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &opt_val, sizeof(opt_val)); 
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	server_addr.sin_port = htons(port);
	if(bind(sd,(struct sockaddr*)&server_addr, sizeof server_addr) == -1){
		perror("Bind error");
		return -1;
	}

	return sd;
}

void afficherInfosConnexion() {
	printf("Information de connexion :\n");
	printf("\tEtat de la connexion : %d\n", maConnexion.etat);
	printf("\tPort serveur : %u\n",  ntohs(maConnexion.port_serveur));
	printf("\tPort client : %u\n",  ntohs(maConnexion.port_client));
	printf("\tAdresse serveur : %lu\n", maConnexion.adresse_serveur);
	printf("\tAdresse client : %lu\n\n", maConnexion.adresse_client);
}

void afficherInfosPaquet(struct tcp_packet paquetAnalyse) {
	printf("Information paquet :\n");
	printf("\tPaquet recu, port_source: %u\n", ntohs(paquetAnalyse.port_source));
	printf("\tPaquet recu, port_destination: %u\n", ntohs(paquetAnalyse.port_destination));
	printf("\tPaquet recu, numero_sequence: %u\n", paquetAnalyse.numero_sequence);
	printf("\tPaquet recu, numero_ack: %u\n", paquetAnalyse.numero_ack);
	printf("\tPaquet recu, DATA: %s\n", paquetAnalyse.data);
}

void modifierEtatTCP(EtatTcp flag_OPT) {
	switch(flag_OPT) {
		case CLOSED :
			maConnexion.etat = CLOSED;
			break;
		case SYNSENT :
			maConnexion.etat = SYNSENT;
			break;
		case SYNRECEIVED :
			maConnexion.etat = SYNRECEIVED;
			break;
		case ESTABLISHED :
			maConnexion.etat = ESTABLISHED;
			break;
		default :
			maConnexion.etat = -1;
			break;
	}
}

int testEtatTCP(){
	return maConnexion.etat;
}